/**
 * Copyright [2019-Present] [starBlues]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.gitee.starblues.loader;

import com.gitee.starblues.loader.jar.AbstractJarFile;
import com.gitee.starblues.loader.jar.JarFileWrapper;
import com.gitee.starblues.loader.utils.IOUtils;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 插件资源存储者
 *
 * @author starBlues
 * @version 3.1.2
 * @since 3.0.0
 */
public class PluginResourceStorage {
    public final static Map<String, Storage> STORAGE_MAP = new ConcurrentHashMap<>();
    private static final Map<String, WeakReference<JarFileWrapper>> WEAK_CACHE = new ConcurrentHashMap<>();
    private static volatile ScheduledExecutorService cleanupScheduler;
    private static volatile ScheduledFuture<?> cleanupFuture;
    private static final AtomicBoolean initialized = new AtomicBoolean(false);
    private static final Object LOCK = new Object();
    // 清理配置
    private static CleanupConfig cleanupConfig = new CleanupConfig();

    /**
     * 添加插件资源
     *
     * @param pluginId 插件id
     * @param pluginFileName 插件文件名称
     */
    public static void addPlugin(String pluginId, String pluginFileName, List<String> libPath) {
        // 确保已初始化
        if (!initialized.get() && cleanupConfig.isEnabled()) {
            initialize();
        }
        if (STORAGE_MAP.containsKey(pluginId)) {
            return;
        }
        STORAGE_MAP.put(pluginId, new Storage(pluginFileName, libPath));
    }

    /**
     * 移除插件
     *
     * @param pluginId 插件
     */
    public static void removePlugin(String pluginId) {
        Storage storage = STORAGE_MAP.get(pluginId);
        if (storage == null) {
            return;
        }
        String pluginFileName = storage.getPluginFileName();
        WEAK_CACHE.entrySet()
            .removeIf(entry -> entry.getKey().contains(pluginFileName) || storage.getLibPath()
                .stream()
                .anyMatch(lib -> entry.getKey().contains(lib)));
        IOUtils.closeQuietly(storage);
        STORAGE_MAP.remove(pluginId);
        System.gc();
    }

    /**
     * 添加插件jar文件
     *
     * @param jarFile jar插件文件
     */
    public static void addJarFile(AbstractJarFile jarFile) {
        if (!(jarFile instanceof JarFileWrapper)) {
            return;
        }
        JarFileWrapper wrapper = (JarFileWrapper) jarFile;
        String jarFilePath = wrapper.getName();
        // JDK 17优化：同时维护弱引用缓存
        WEAK_CACHE.put(jarFilePath, new WeakReference<>(wrapper));
        STORAGE_MAP.forEach((k, v) -> {
            v.addJarFile(jarFile);
        });
    }

    /**
     * 通过 JAR 文件路径获取已缓存的 JarFileWrapper
     *
     * @param jarFilePath JAR 文件路径
     * @return 已缓存的 JarFileWrapper，如果不存在返回 null
     */
    public static JarFileWrapper getCachedJarFile(String jarFilePath) {
        // 首先检查弱引用缓存
        WeakReference<JarFileWrapper> weakRef = WEAK_CACHE.get(jarFilePath);
        if (weakRef != null) {
            JarFileWrapper wrapper = weakRef.get();
            if (wrapper != null) {
                return wrapper;
            } else {
                // 清理过期的弱引用
                WEAK_CACHE.remove(jarFilePath);
            }
        }
        // 回退到常规缓存
        for (Storage storage : STORAGE_MAP.values()) {
            JarFileWrapper jarFile = storage.getCachedJarFile(jarFilePath);
            if (jarFile != null) {
                return jarFile;
            }
        }
        return null;
    }

    /**
     * 添加插件根的jar文件
     *
     * @param file 插件文件
     * @param jarFile 插件jar文件
     */
    public static void addRootJarFile(File file, JarFileWrapper jarFile) {
        STORAGE_MAP.forEach((k, v) -> {
            v.addRootJarFile(file, jarFile);
        });
    }

    /**
     * 通过插件文件获取插件jar文件
     *
     * @param file 插件文件
     * @return 插件jar文件
     */
    public static JarFileWrapper getRootJarFile(File file) {
        for (Storage value : STORAGE_MAP.values()) {
            JarFileWrapper jarFile = value.getRootJarFile(file);
            if (jarFile != null) {
                return jarFile;
            }
        }
        return null;
    }

    /**
     * 获取所有缓存的JAR文件信息（用于调试或监控）
     *
     * @return 所有缓存的JAR文件名称列表
     */
    public static List<String> getAllCachedJarFiles() {
        List<String> result = new ArrayList<>();
        for (Storage storage : STORAGE_MAP.values()) {
            result.addAll(storage.getAllCachedJarFileNames());
        }
        return result;
    }

    /**
     * 配置清理任务
     */
    public static void configureCleanup(CleanupConfig config) {
        if (config == null) {
            return;
        }
        synchronized (LOCK) {
            cleanupConfig = config;
            // 如果已经初始化，重新调度
            if (initialized.get()) {
                stopCleanup();
                if (config.isEnabled()) {
                    startCleanup();
                }
            }
        }
    }

    /**
     * 启动清理任务
     */
    private static void startCleanup() {
        synchronized (LOCK) {
            if (cleanupScheduler == null) {
                cleanupScheduler = Executors.newSingleThreadScheduledExecutor(new CleanupThreadFactory());
            }
            if (cleanupFuture != null) {
                cleanupFuture.cancel(false);
            }
            Runnable cleanupTask = () -> {
                try {
                    performCleanup(cleanupConfig.getTimeoutMillis());
                } catch (Exception e) {
                    if (cleanupConfig.isLogEnabled()) {
                        System.err.println("PluginResourceStorage cleanup task error: " + e.getMessage());
                    }
                }
            };
            cleanupFuture = cleanupScheduler.scheduleAtFixedRate(cleanupTask, cleanupConfig.getInitialDelayMillis(),
                cleanupConfig.getIntervalMillis(), TimeUnit.MILLISECONDS);
            initialized.set(true);
            if (cleanupConfig.isLogEnabled()) {
                System.out.println(
                    "PluginResourceStorage cleanup started: " + "initialDelay=" + cleanupConfig
                    .getInitialDelayMillis()
                        + "ms, " + "interval=" + cleanupConfig.getIntervalMillis() + "ms, " + "timeout="
                        + cleanupConfig.getTimeoutMillis() + "ms");
            }
        }
    }

    /**
     * 手动触发立即清理
     */
    public static void triggerImmediateCleanup() {
        performCleanup(cleanupConfig.getTimeoutMillis());
    }

    /**
     * 获取清理任务状态
     */
    public static String getCleanupStatus() {
        return String.format("Cleanup: initialized=%s, enabled=%s, active=%s", initialized.get(),
            cleanupConfig.isEnabled(),
            cleanupFuture != null && !cleanupFuture.isCancelled() && !cleanupFuture.isDone());
    }

    /**
     * 执行清理操作
     */
    private static void performCleanup(long timeoutMillis) {
        // int totalCleaned = 0;
        // for (Storage storage : STORAGE_MAP.values()) {
        //     totalCleaned += storage.cleanupUnused(timeoutMillis);
        // }
        // 清理弱引用缓存中的过期条目
        int weakRefCleaned = 0;
        Iterator<Map.Entry<String, WeakReference<JarFileWrapper>>> iterator = WEAK_CACHE.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, WeakReference<JarFileWrapper>> entry = iterator.next();
            if (entry.getValue().get() == null) {
                iterator.remove();
                weakRefCleaned++;
            }
        }
        // if (cleanupConfig.isLogEnabled() && (totalCleaned > 0 || weakRefCleaned > 0)) {
        //     System.out.println(
        //         "PluginResourceStorage cleanup: " + totalCleaned + " jar files cleaned, " + weakRefCleaned
        //             + " weak references removed");
        // }
        if (cleanupConfig.isLogEnabled() && weakRefCleaned > 0) {
            System.out.println("PluginResourceStorage cleanup: " + weakRefCleaned + " weak references removed");
        }
    }

    /**
     * 停止清理任务
     */
    private static void stopCleanup() {
        synchronized (LOCK) {
            if (cleanupFuture != null) {
                cleanupFuture.cancel(false);
                cleanupFuture = null;
            }
            if (cleanupScheduler != null) {
                cleanupScheduler.shutdown();
                try {
                    if (!cleanupScheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                        cleanupScheduler.shutdownNow();
                    }
                } catch (InterruptedException e) {
                    cleanupScheduler.shutdownNow();
                    Thread.currentThread().interrupt();
                }
                cleanupScheduler = null;
            }
            initialized.set(false);
            if (cleanupConfig.isLogEnabled()) {
                System.out.println("PluginResourceStorage cleanup stopped");
            }
        }
    }

    /**
     * 初始化清理任务（由插件管理器调用）
     */
    public static void initialize() {
        synchronized (LOCK) {
            if (initialized.get()) {
                return;
            }
            if (cleanupConfig.isEnabled()) {
                startCleanup();
            }
            // 注册关闭钩子
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                shutdown();
            }, "PluginResourceStorage-Shutdown"));
        }
    }

    /**
     * 关闭整个资源存储系统
     */
    public static void shutdown() {
        synchronized (LOCK) {
            // 停止清理任务
            stopCleanup();
            // 关闭所有存储
            for (Storage storage : STORAGE_MAP.values()) {
                IOUtils.closeQuietly(storage);
            }
            STORAGE_MAP.clear();
            WEAK_CACHE.clear();
            System.out.println("PluginResourceStorage shutdown completed");
        }
    }

    private static class Storage implements Closeable {
        private final String pluginFileName;
        private final List<String> libPath;

        // 统一的缓存结构：文件名 -> JarFileWrapper
        private final Map<String, JarFileWrapper> jarFileCache = new ConcurrentHashMap<>();

        // 文件对象到文件名的映射，用于支持通过File对象查找
        private final Map<File, String> fileToNameMap = new ConcurrentHashMap<>();
        // JDK 17优化：添加访问时间追踪
        // private final ConcurrentHashMap<String, Long> accessTimeMap = new ConcurrentHashMap<>();

        public Storage(String pluginFileName, List<String> libPath) {
            this.pluginFileName = pluginFileName;
            this.libPath = libPath == null ? Collections.emptyList() : new ArrayList<>(libPath);
        }

        public List<String> getLibPath() {
            return Collections.unmodifiableList(libPath);
        }

        public String getPluginFileName() {
            return pluginFileName;
        }

        /**
         * 添加JAR文件到缓存
         */
        public void addJarFile(AbstractJarFile jarFile) {
            if (jarFile == null || !(jarFile instanceof JarFileWrapper)) {
                return;
            }
            String name = jarFile.getName();
            if (name == null || !isAddFile(name)) {
                return;
            }
            // accessTimeMap.put(name, System.currentTimeMillis());
            // 使用putIfAbsent避免重复添加
            jarFileCache.putIfAbsent(name, (JarFileWrapper) jarFile);
            // debug
            // JarFileWrapper existing = jarFileCache.putIfAbsent(name, (JarFileWrapper) jarFile);
            // if (existing == null) {
            //     System.out.println("PluginResourceStorage: cached jar file name=" + name);
            // }
        }

        /**
         * 添加根JAR文件
         */
        public void addRootJarFile(File file, JarFileWrapper jarFile) {
            if (file == null || jarFile == null) {
                return;
            }
            String absolutePath = file.getAbsolutePath();
            if (!isAddFile(absolutePath)) {
                return;
            }
            String jarFileName = jarFile.getName();
            // 维护文件对象到文件名的映射
            fileToNameMap.put(file, jarFileName);
            // 添加到统一缓存
            jarFileCache.putIfAbsent(jarFileName, jarFile);
            // debug
            // JarFileWrapper existing = jarFileCache.putIfAbsent(jarFileName, jarFile);
            // if (existing == null) {
            //     System.out.println("PluginResourceStorage: cached jar file name=" + jarFileName);
            // }
        }

        /**
         * 通过文件路径获取缓存的JAR文件
         */
        public JarFileWrapper getCachedJarFile(String jarFilePath) {
            // JDK 17优化：更新访问时间
            // accessTimeMap.put(jarFilePath, System.currentTimeMillis());
            return jarFileCache.get(jarFilePath);
        }

        /**
         * 通过文件对象获取根JAR文件
         */
        public JarFileWrapper getRootJarFile(File file) {
            String fileName = fileToNameMap.get(file);
            if (fileName != null) {
                return jarFileCache.get(fileName);
            }
            return null;
        }

        /**
         * JDK 17优化：清理长时间未使用的JAR文件
         */
        public int cleanupUnused(long timeoutMillis) {
            // long currentTime = System.currentTimeMillis();
            // List<String> toRemove = new ArrayList<>();
            // for (Map.Entry<String, Long> entry : accessTimeMap.entrySet()) {
            //     if (currentTime - entry.getValue() > timeoutMillis) {
            //         toRemove.add(entry.getKey());
            //     }
            // }
            // int cleanedCount = 0;
            // for (String jarFileName : toRemove) {
            //     JarFileWrapper jarFile = jarFileCache.remove(jarFileName);
            //     if (jarFile != null) {
            //         System.out.println("JDK17-Optimized: cleaning unused jar file: " + jarFileName);
            //         jarFile.canClosed();
            //         IOUtils.closeQuietly(jarFile);
            //         cleanedCount++;
            //     }
            //     fileToNameMap.entrySet().removeIf(e -> e.getValue().equals(jarFileName));
            // }
            // return cleanedCount;
            return 0;
        }

        /**
         * 获取所有缓存的JAR文件名称（用于调试）
         */
        public List<String> getAllCachedJarFileNames() {
            return new ArrayList<>(jarFileCache.keySet());
        }

        @Override
        public void close() throws IOException {
            // 关闭所有缓存的JAR文件
            jarFileCache.forEach((key, jarFile) -> {
                if (jarFile != null) {
                    jarFile.canClosed();
                    IOUtils.closeQuietly(jarFile);
                }
            });
            // 清空缓存
            jarFileCache.clear();
            fileToNameMap.clear();
            // accessTimeMap.clear();
        }

        /**
         * 检查文件路径是否应该被添加到当前存储中
         */
        private boolean isAddFile(String path) {
            if (path == null) {
                return false;
            }
            if (path.contains(pluginFileName)) {
                return true;
            }
            for (String lib : libPath) {
                if (path.contains(lib)) {
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * 清理配置
     */
    public static class CleanupConfig {
        private boolean enabled = true;
        private long initialDelayMillis = 2 * 60 * 1000;  // 2分钟后开始
        private long intervalMillis = 5 * 60 * 1000;     // 5分钟间隔
        private long timeoutMillis = 10 * 60 * 1000;      // 10分钟超时
        private boolean logEnabled = true;

        public boolean isEnabled() {
            return enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }

        public long getInitialDelayMillis() {
            return initialDelayMillis;
        }

        public void setInitialDelayMillis(long initialDelayMillis) {
            this.initialDelayMillis = initialDelayMillis;
        }

        public long getIntervalMillis() {
            return intervalMillis;
        }

        public void setIntervalMillis(long intervalMillis) {
            this.intervalMillis = intervalMillis;
        }

        public long getTimeoutMillis() {
            return timeoutMillis;
        }

        public void setTimeoutMillis(long timeoutMillis) {
            this.timeoutMillis = timeoutMillis;
        }

        public boolean isLogEnabled() {
            return logEnabled;
        }

        public void setLogEnabled(boolean logEnabled) {
            this.logEnabled = logEnabled;
        }
    }

    private static class CleanupThreadFactory implements ThreadFactory {
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        public CleanupThreadFactory() {
            namePrefix = "PluginResourceStorage-Cleanup-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
            t.setDaemon(true);
            t.setPriority(Thread.MIN_PRIORITY);
            return t;
        }
    }

}